﻿using System;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.UI;
using System.Web.UI.WebControls;

using IdeaNMR.Web.Core;

namespace IdeaNMR.Web
{
    // ALISTAIR: comments added 12/07/2011
    // Class for interfacing between the Experiment table of the database
    // and the "My Experiments" table displayed to user
    public partial class ExperimentsTable : System.Web.UI.UserControl
    {
        // embedded class with a single experiment as a property
        protected class QueryItem
        {
            public Experiment Obj { get; set; }
        }

        /// <summary>
        /// The event is fired when user clicks on an entry in a table and Selectable is true.
        /// </summary>
        // Simple embedded class to keep track of the currently selected experiment
        public event ItemSelectedEventHandler Selected;
        public delegate void ItemSelectedEventHandler(object sender, ItemSelectedCommandEventArgs e);
        public class ItemSelectedCommandEventArgs : EventArgs
        {
            // declare a property representing the currently
            // selected NMRObject as an experiment
            public Experiment SelectedNMRObject
            {
                get;
                private set;
            }

            // When a specified experiment is selected,
            // make this experiment the currently selected
            // NMRObject
            public ItemSelectedCommandEventArgs(Experiment o)
            {
                this.SelectedNMRObject = o;
            }
        }

        /// <summary>
        /// If true, then on clicking the row a Selected event is fired.
        /// Also visual effects are added to the table.
        /// </summary>
        [System.ComponentModel.DefaultValue(false)]
        // declare a flag to indicate if the Experiment is "selectable",
        // based on value from the ViewState
        public bool Selectable
        {
            get
            {
                bool? res = ViewState["Selectable"] as bool?;
                if (res.HasValue) return res.Value;
                else return false;
            }
            set
            { ViewState["Selectable"] = value; }
        }

        /// <summary>
        /// Number of items on one page
        /// </summary>
        [System.ComponentModel.DefaultValue(15)]
        public int PageSize
        {
            get
            {
                if (ViewState["PageSize"] is int)
                    return (int)ViewState["PageSize"];
                else return 15;
            }
            set { ViewState["PageSize"] = value; }
        }

        /// <summary>
        /// The search string
        /// </summary>
        /// Default value is string.Empty
        // The user can use search terms to filter the list of "My Experiments".
        // The getter simply splits the search string into a list of words,
        // and returns these a list of strings
        // The setter takes the list and concatenates to a single string, with spaces
        // between each word, writing the result to the search field.
        private string[] SearchTerms
        {
            get
            {
                string text = SearchBox.Text;
                //  string text = ((TextBox)ContentListView.FindControl("SearchBox")).Text;
                if (!string.IsNullOrEmpty(text))
                {
                    return text.ToLowerInvariant().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                }
                else return new string[0];
            }
            set { ((TextBox)ContentListView.FindControl("SearchBox")).Text = string.Join(" ", value); }
        }

        /// <summary>
        /// Refreshes table
        /// </summary>
        public void Refresh()
        {
            this._bulkData = QueryDBForBulkData();
            ContentListView.DataBind();
        }

        /// <summary>
        /// The list of all db entries of this.Type
        /// that user has an access to
        /// </summary>
        /// <remarks>
        /// Storing this in ViewState makes page to responde very slow
        /// Storing this in Session can consume lots of memory
        /// </remarks>
        private QueryItem[] BulkData
        {
            get
            {
                if (this._bulkData == null) this._bulkData = QueryDBForBulkData();
                return this._bulkData;
            }
        }

        // declare the list of experiments as an array of QueryItems
        private QueryItem[] _bulkData;

        // Perform an SQL query to the database to retrieve all the experiments
        // created by the user, returned as a list of QueryItems
        private QueryItem[] QueryDBForBulkData()
        {
            // establish a new connection to the database
            IdeaNMRdbContainer inmr = new IdeaNMRdbContainer();

            // execute the SQL query to select all the experiments for the user,
            // generating a new QueryItem object for each experiment
            var exps = from Experiment exp in inmr.Experiments
                       where exp.User.Username == ScrewTurnWikiFacade.Username
                       select new QueryItem
                       {
                           Obj = exp
                       };

            // convert the experiments to a list of individual QueryItems
            var result = exps.ToList();
            
            // for each of the experiments, make sure a reference to the corresponding
            // spectrometer, sample and pulse program has been entered
            result.ForEach(x =>
                {
                    if (!x.Obj.SpectrometerReference.IsLoaded) x.Obj.SpectrometerReference.Load();
                    if (!x.Obj.SampleReference.IsLoaded) x.Obj.SampleReference.Load();
                    if (!x.Obj.PulseProgramReference.IsLoaded) x.Obj.PulseProgramReference.Load();
                });

            // return the experiments as an array of QueryItems
            return result.ToArray();
        }

        // Filter the array of user experiments based on the search criteria
        // that was entered.
        private QueryItem[] GetFilteredData()
        {
            // copy the array of user experiments to local variable
            var result = this.BulkData;

            if (this.SearchTerms.Length > 0)
            {
                /// Search Pattern - Multiple word beginings
                // The result pattern regular expression consists of the startline character (^),
                // followed by any of the words in the SearchTerms string array (the search string).
                // For regular expressions, the dot (.) needs to be escaped to be interpreted literally (\.)
                string rPat = @"^" + string.Join("|^", this.SearchTerms).Replace(".", @"\.");
                
                // Perform a SQL query on the full list of user experiments, returning only
                // the experiments where one of the following matches the regular expression:
                // The experiment name
                // The spectrometer name
                // The sample name
                // The pulse program name
                // The timestamp for creation of the experiment.
                // Should we also filter based on the 'Remarks'??
                result = (from QueryItem r in result
                          where
                          Regex.IsMatch(r.Obj.Name.ToLowerInvariant(), rPat) ||
                          Regex.IsMatch(r.Obj.Spectrometer.Name.ToLowerInvariant(), rPat) ||
                          Regex.IsMatch(r.Obj.Sample.Name.ToLowerInvariant(), rPat) ||
                          Regex.IsMatch(r.Obj.PulseProgram.Name.ToLowerInvariant(), rPat) ||
                          r.Obj.Datetime.ToString().ToLowerInvariant().Split(' ').Any(d => Regex.IsMatch(d, rPat))
                          select r).ToArray();
            }
            // return the array of filtered results
            return result;
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                TopDataPager.PageSize = BottomDataPager.PageSize = this.PageSize;
            }
        }


        #region Control Events
        protected void Refresh_Click(object sender, EventArgs e)
        {
             this.Refresh();
        }

        protected void SearchButton_Click(object sender, EventArgs e)
        {
            ContentListView.DataBind();
            Control wc = ContentListView.FindControl("SearchBox");
            TextBox tb = wc as TextBox;
            if (tb == null) return;
            tb.Focus();
        }

        protected void ldsExperiments_OnSelect(object sender, LinqDataSourceSelectEventArgs e)
        {
            e.Result = this.GetFilteredData();
        }

        protected void ContentListView_ItemDataBound(object sender, ListViewItemEventArgs e)
        {
            if (e.Item.ItemType == ListViewItemType.DataItem && this.Selectable)
            {
                /// Copy onclick event from the button to the panel, so that clicking on the whole panel will
                /// cause the postback
                Button btn = e.Item.FindControl("InvisibleCommandButton") as Button;
                string script = this.Page.ClientScript.GetPostBackClientHyperlink(btn, string.Empty, false);
                Panel p = e.Item.FindControl("ItemPanel") as Panel;
                p.Attributes.Add("onclick", script);
            }
        }
        
        protected void Item_Command(object sender, CommandEventArgs e)
        {
            if (e.CommandName != "PickItem") return;
            Guid target = new Guid(e.CommandArgument.ToString());
            Experiment o = this.BulkData.Where(b => b.Obj.Id == target).Single().Obj;

            if (o == null) return;
            if (this.Selected != null) Selected(this, new ItemSelectedCommandEventArgs(o));
        }
        #endregion

        #region ListView Data Evaluation
        protected string CreateCssClass(bool IsDirect)
        {
            StringBuilder res = new StringBuilder("ExperimentsTable Item");
            res.AppendFormat(" {0} {1}", IsDirect ? "Direct" : "Alter", this.Selectable ? "Selectable" : string.Empty);
            return res.ToString();
        }
        #endregion

        protected string CreatePulseProgramLink(object ppObj)
        {
            PulseProgram pp = ppObj as PulseProgram;
            if (pp == null) throw new ArgumentException("The object is not of type pulse program. It is " + ppObj.ToString());
            if (!pp.FirstVersionReference.IsLoaded) pp.FirstVersionReference.Load();
            return pp.FirstVersion.IsPrivate
                       ? string.Format("javascript: parent.location='/PulseProgram_New.ashx?TargetID={0}';", pp.Id)
                       : string.Format("javascript: parent.location='/PulseProgram_id_{0}.ashx?TargetID={1}';", pp.FirstVersion.Id, pp.Id);
        }

        protected string CreateSampleLink(object sampObj)
        {
            Sample s = sampObj as Sample;
            if (s == null) throw new ArgumentException("The object is not of type Sample. It is " + sampObj.ToString());

            if (!s.FirstVersionReference.IsLoaded) s.FirstVersionReference.Load();
            return s.FirstVersion.IsPrivate
                        ? string.Format("javascript: parent.location='/Sample_New.ashx?TargetID={0}';", s.Id)
                        : string.Format("javascript: parent.location='/Sample_id_{0}.ashx?TargetID={1}';", s.FirstVersion.Id, s.Id);
        }

        protected string CreateSpectrometerLink(object specObj)
        {
            Spectrometer s = specObj as Spectrometer;
            if (s == null) throw new ArgumentException("The object is not of type Spectrometer. It is " + specObj.ToString());
            if (!s.FirstVersionReference.IsLoaded) s.FirstVersionReference.Load();

            return s.FirstVersion.IsPrivate
                        ? string.Format("javascript: parent.location='/Spectrometer_New.ashx?TargetID={0}';", s.Id)
                        : string.Format("javascript: parent.location='/Spectrometer_id_{0}.ashx?TargetID={1}';", s.FirstVersion.Id, s.Id);
        }

    } // end ExperimentsTable class

} // end IdeaNMR.Web namespace